package com.ldzy.webase.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.ldzy.webase.annotation.PassToken;
import com.ldzy.webase.annotation.UserLoginToken;
import com.ldzy.webase.exception.ExceptionResponse;
import com.ldzy.webase.exception.ExceptionResponseCode;
import com.ldzy.webase.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @title:AuthenticationInterceptor
 * @description:认证请求头中的token的拦截器类
 * @Author：Lq
 */
@Service
public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;

    /**
     * 执行目标方法之前拦截验证token
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param object
     * @return
     * @throws Exception
     */
    // 重写方法，实现拦截
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object)
            throws Exception {
        //从http请求头中取出token
        String token = httpServletRequest.getHeader("Authorization");
        //如果不是映射到方法(即-路径下的方法)直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) object;
        //拿到方法头部的注解
        Method method = handlerMethod.getMethod();
        //检查是否有PassToken注释，有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            // 进入到PassToken的注解中，查看里面required的值是否为true（默认true）
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                Timestamp timestamp = new Timestamp(new Date().getTime()); // 控制台的时间线
                System.out.println(String.format("\033[%dm%s\033[0m", 36, timestamp) + "\t" + String.format("\033[%dm%s\033[0m", 31, httpServletRequest.getRequestURI() + " ：@PassToken跳过认证"));

                return true;
            } else {
                throw ExceptionResponse.build(ExceptionResponseCode.ERROR_NOT_TOKEN);// (401, "服务器开小差了")
            }
        }

        //检查是否有UserLoginToken注释，有则认证
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            // 进入到UserLoginToken的注解中，查看里面required的值是否为true（默认true）
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                //执行认证
                Timestamp timestamp = new Timestamp(new Date().getTime()); // 控制台的时间线
                // 控制台输出的内容，时间线+红字内容
                System.out.println(String.format("\033[%dm%s\033[0m", 36, timestamp) + "\t" + String.format("\033[%dm%s\033[0m", 31, httpServletRequest.getRequestURI() + " ：@UserLoginToken执行认证"));
//                System.out.println("Authorization ：" + token);
                //如果没有token，则代表当前状态为未登录
                if (token == null || token.equals("")) {
                    throw ExceptionResponse.build(ExceptionResponseCode.ERROR_NOT_LOGIN);// (500, "还未登陆，请先登陆")
                }
                //如果有token，则取出token中的id和pwd
                int uid; // 存储从token取出的uid
                String userPassword; // 存储根据uid从数据库取出的密码，用于验证token密钥


                //解密token，取出放进token中携带的第1个参数（即存入的 user.uid）
                uid = Integer.parseInt(JWT.decode(token).getAudience().get(0));
//                System.out.println("uid的值" + uid);

                //若取出过程出错，则报一个错误
//                throw ExceptionResponse.build(ExceptionResponseCode.ERROR_NOT_TOKEN);// (401, "服务器开小差了")

                //成功取出uid，则根据uid调用service层的getPasswordByUid（根据用户uid查询密码并返回）方法
                userPassword = userService.getPasswordByUid(uid);

                // 密码查询不出，则 token 验证失败
                if (userPassword.length() == 0) {
                    throw ExceptionResponse.build(ExceptionResponseCode.ERROR_NOT_USER);// (401, "用户不存在，请重新登录")
                }
                // 如果账号存在，此时验证密码，查看数据库中的密码是否和token中保存的密码一致
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(userPassword)).build();
                try {
                    // 将数据库中的密钥 跟 token中的密钥 进行比对验证
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw ExceptionResponse.build(ExceptionResponseCode.ERROR_PAST_USERINFO);// (401, "账户信息已过期，请重新登陆");
                }

                // 验证用户上次最后的登陆时间是否超过一天
                long currentTime = new Date().getTime(); // 当前时间戳
                long endLoginTime = userService.getEndLoginTimeByUid(uid).getTime(); // 数据库的时间戳
                if (currentTime - endLoginTime > 86400000) { // 一天的时间戳为86400000
                    throw ExceptionResponse.build(ExceptionResponseCode.ERROR_PAST_USERINFO);// (401, "账户信息已过期，请重新登陆");
                } else {
                    // 不超过一天，则更新时间
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String currentTimeStr = sdf.format(new Date());
                    userService.editEndLoginTimeByUid(currentTimeStr, uid);
                }
            } else {
                throw ExceptionResponse.build(ExceptionResponseCode.ERROR_NOT_TOKEN);// (401, "服务器开小差了")
            }
            // 验证成功，放行
            return true;
        }
        // 无注释，默认放行
        return true;
    }
}

